//
//  Encrypt.m
//  APJ Resource
//
//  Created by Richard Zhao on 2/10/14.
//  Copyright (c) 2014 APJ. All rights reserved.
//

#import "Encrypt.h"
#import "MF_Base64Additions.h"
#import <CommonCrypto/CommonDigest.h>

@implementation Encrypt

+ (BOOL)isHashMatchedFrom:(NSString *)plainText to:(NSString *)hasedText
{
    BOOL result = NO;
    NSData *plainData = [plainText dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
    NSData *hasedData = [MF_Base64Codec dataFromBase64String:hasedText];
    result = [Encrypt compareText:plainData withHash:hasedData];
    
    return result;
}

+ (NSData *)computeHashWith:(NSData *)plainText
{
    uint8_t result[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512(plainText.bytes, plainText.length, result);
    return [NSData dataWithBytes:result length:CC_SHA512_DIGEST_LENGTH];
}

+ (NSData *)combineBytes:(NSData *)b1 with:(NSData *)b2
{
    NSMutableData *result = [NSMutableData dataWithData:b1];
    [result appendData:b2];
    //[result appendBytes:b2.bytes length:b2.length];
    
 
    return result;
}

+ (NSData *)createHashFrom:(NSData *)plainText with:(NSData *)salt
{
    NSData *temp = [Encrypt combineBytes:salt with:plainText];
    NSData *hash = [Encrypt computeHashWith:temp];
    NSData *result = [Encrypt combineBytes:salt with:hash];
    
    return result;
}

+ (NSData *)extractSaltFrom:(NSData *)hashedText
{
    if (hashedText.length > SALT_LENGTH)
    {
        NSData *salt = [[NSData alloc] initWithBytes:hashedText.bytes length:SALT_LENGTH];
        return salt;
    }
    return  nil;
}

+ (BOOL)compareBytes:(NSData *)b1 with:(NSData *)b2
{
    return [b1 isEqualToData:b2];
}

+ (BOOL)compareText:(NSData *)plainText withHash:(NSData *)hashedText
{
    BOOL result = NO;
    NSData *salt = [Encrypt extractSaltFrom:hashedText];
    NSData *hashedPlainText = [Encrypt createHashFrom:plainText with:salt];
    result = [Encrypt compareBytes:hashedPlainText with:hashedText];
    
    return result;
}

+ (NSData *)base64Decode:(NSString *)string
{
    unsigned long ixtext, lentext;
    unsigned char ch, inbuf[4], outbuf[4];
    short i, ixinbuf;
    Boolean flignore, flendtext = false;
    const unsigned char *tempcstring;
    NSMutableData *theData;
    
    if (string == nil) {
        return [NSData data];
    }
    
    ixtext = 0;
    
    tempcstring = (const unsigned char *)[string UTF8String];
    
    lentext = [string length];
    
    theData = [NSMutableData dataWithCapacity: lentext];
    
    ixinbuf = 0;
    
    while (true) {
        if (ixtext >= lentext){
            break;
        }
        
        ch = tempcstring [ixtext++];
        
        flignore = false;
        
        if ((ch >= 'A') && (ch <= 'Z')) {
            ch = ch - 'A';
        } else if ((ch >= 'a') && (ch <= 'z')) {
            ch = ch - 'a' + 26;
        } else if ((ch >= '0') && (ch <= '9')) {
            ch = ch - '0' + 52;
        } else if (ch == '+') {
            ch = 62;
        } else if (ch == '=') {
            flendtext = true;
        } else if (ch == '/') {
            ch = 63;
        } else {
            flignore = true;
        }
        
        if (!flignore) {
            short ctcharsinbuf = 3;
            Boolean flbreak = false;
            
            if (flendtext) {
                if (ixinbuf == 0) {
                    break;
                }
                
                if ((ixinbuf == 1) || (ixinbuf == 2)) {
                    ctcharsinbuf = 1;
                } else {
                    ctcharsinbuf = 2;
                }
                
                ixinbuf = 3;
                
                flbreak = true;
            }
            
            inbuf [ixinbuf++] = ch;
            
            if (ixinbuf == 4) {
                ixinbuf = 0;
                
                outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
                outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
                outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);
                
                for (i = 0; i < ctcharsinbuf; i++) {
                    [theData appendBytes: &outbuf[i] length: 1];
                }
            }
            
            if (flbreak) {
                break;
            }
        }
    }
    
    return theData;
}

@end
